﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kokomo.Environment;
using System.Windows.Input;
using System.Windows.Documents;
using System.Threading;
using System.IO;
using System.Windows;
using System.Windows.Media;
using Kokomo.Environment.Services;
using Kokomo.Environment.Documents;

namespace Kokomo.PeInspector.Reflection
{
	[ViewType(typeof(SourceDocumentView))]
	public abstract class FlowDocumentViewModelBase : NotifierObject
	{
		protected FlowDocumentViewModelBase(
			NavigationContext navigationContext,
			string title
			)
		{
			if (navigationContext == null) throw new ArgumentNullException("navigationContext");

			this.NavigationContext = navigationContext;
			this.SaveDocumentCommand = new DelegatedCommand(this.SaveDocument);
			this.Title = title;

			this.RebuildCommand = new DelegatedCommand(this.Rebuild);
		}

		public string Title { get; private set; }
		public NavigationContext NavigationContext { get; private set; }
		private FlowDocument _sourceDocument;
		protected FlowDocument DocumentInternal { get { return this._sourceDocument; } }
		public FlowDocument Document
		{
			get
			{
				if (
					(this._sourceDocument == null)
					&& (!this.IsLoading)
					)
				{
					this.IsLoading = true;
					new Thread(this.BuildSourceDocumentAsyncProc) { Name = "FlowDocumentViewModel.BuildSourceDocument" }.Start();
				}

				return this._sourceDocument;
			}
			private set
			{
				this._sourceDocument = value;
				this.OnPropertyChanged("Document");
				this.IsLoading = false;
			}
		}

		protected virtual FlowDocument CreateSourceDocument()
		{
			FlowDocument document = new FlowDocument();
			return document;
		}

		protected abstract void BuildDocumentAsync(FlowDocument document);
		private void BuildSourceDocumentAsyncProc()
		{
			try
			{
				FlowDocument document = (FlowDocument)Application.Current.Dispatcher.Invoke((Func<FlowDocument>)this.CreateSourceDocument);
				if (document != null)
				{
					this.BuildDocumentAsync(document);
					this.Document = document;
				}
			}
			catch (Exception ex)
			{
				ErrorDocumentViewModel errorDoc = new ErrorDocumentViewModel()
				{
					MainCaption = UiString.FromString(Messages.ErrorGeneratingDocumentText),
					Detail = UiString.FromString(Messages.ErrorGeneratingDocumentDetailText),
					Exception = ex
				};
			}
			this.IsLoading = false;
		}

		private bool _isLoading;
		public bool IsLoading
		{
			get { return this._isLoading; }
			private set
			{
				this._isLoading = value;
				this.OnPropertyChanged("IsLoading");
			}
		}

		public ICommand SaveDocumentCommand { get; private set; }
		protected virtual void SaveDocument()
		{
			FileDialogOptions options = new FileDialogOptions()
			{
				FileTypes = new FileType[] { AssemblyAnalyzerEditor.FlowDocumentFileType },
				Title = UiString.FromString(Messages.SaveSourceFileTitle),
				InitialFileName = this.Title
			};
			string sourceFileName = EnvironmentServices.UiService.ShowSaveFileDialog(options);
			if (sourceFileName != null)
			{
				using (StreamWriter writer = new StreamWriter(sourceFileName))
				{
					System.Windows.Markup.XamlWriter.Save(this.Document, writer);
				}
			}
		}

		#region Font
		private FontFamily _fontFamily;
		public FontFamily FontFamily
		{
			get { return this._fontFamily; }
			set
			{
				if (this._fontFamily == value) return;
				this._fontFamily = value;

				FlowDocument document = this.DocumentInternal;
				if (document != null)
				{
					document.FontFamily = value;
				}

				this.OnFontFamilyChanged();
			}
		}
		protected virtual void OnFontFamilyChanged()
		{
			this.OnPropertyChanged("FontName");
		}

		private double _fontSize;
		public double FontSize
		{
			get { return this._fontSize; }
			set
			{
				if (this._fontSize == value) return;
				this._fontSize = value;

				FlowDocument document = this.DocumentInternal;
				if (document != null)
				{
					document.FontSize = value;
				}

				this.OnFontSizeChanged();
			}
		}
		protected virtual void OnFontSizeChanged()
		{
			this.OnPropertyChanged("FontSize");
		}

		public IList<FontFamily> FontFamilyList
		{
			get { return Fonts.SystemFontFamilies.ToArray(); }
		}
		#endregion

		#region Commands
		public ICommand RebuildCommand { get; private set; }
		private void Rebuild()
		{
			this._sourceDocument = null;
			this.OnPropertyChanged("Document");
		}
		#endregion
	}
}
